/**
 * Basic logic for the internal representation of a graph
 */
YUI().use('json-parse','json-stringify', function (Y) {
  GraphApp = (function () {
          var Graph, Node, Vector;

          // Helps keeping a nodes position organized
          Vektor = function (x, y) {
                  this.x = x;
                  this.y = y;
          };

          /**
           * Representation of a single node of a graph
       */
          Node = function (value, id, x, y) {
                  this.id = id;
                  this.pos = new Vektor(x, y);
                  this.value = value;
                  this.children = [];
          };

          Node.prototype = {
                  /**
                   * Removes the edge from this node to the node specified by {id}
                   */
                  _removeEdge : function (id) {
                          for (var i = this.children.length - 1; i >= 0; i--) {
                                  if (this.children[i] === id) {
                                          this.children.splice(i, 1);
                                          //break;
                                  }
                          }
                  },

                  /**
                   * Updates the x and y coordinates of the node
                   */
                  _setCoordinates : function (x, y) {
                          this.pos = new Vektor(x, y);
                  },

                  /**
                   * Adds an edge from this node to the node with the id specified by {to}
                   * TODO: check if already there. Allow edge to node itself?
                   */
                  _addEdge : function (to) {
                          this.children.push(to);
                  },

                  /**
                   * Returns the array of children for this node, which contains the ids of
                   * the nodes to which an edge leads to from this node
                   */
                  _neighbors : function () {
                          return this.children;
                  }
          };

          /**
           * Representation of a complete graph
           */
          Graph = function () {
                  this.nodes = new Array();
                  // An autoincremented id to be able to assign each node an unique id
                  this._nodeId = 0;
          };

          Graph.prototype = {
                  /**
                   * Returns the next (within one graph) unique id
                   */
                  _getNodeId : function () {
                          this._nodeId = this._nodeId + 1;
						  // ids might be in use after importing a graph
						  // make sure to find an unused id
						  while (this.nodes[this._nodeId] !== undefined) {
							this._nodeId = this._nodeId + 1;
						  }
						  console.log(this._nodeId);
                          return this._nodeId;
                  },

                  /**
                   * Given a value and its coordinates add a new node to the graph
                   * On adding a node the node is assigned with a unique id.
                   * @return the node created by this function
                   * TODO: error handling, default coordinates
                   */
                  addNode : function (value, x, y, id) {
						  var nid;
						  if(id == 'undefined' || id == null){
                            nid = this._getNodeId();
                          } else {
							nid = id;
						  }
                          var n = new Node(value, nid, x, y);
                          if (typeof this.nodes[n.id] === "undefined") {
                                  this.nodes[n.id] = n;
                                  return n;
                          }
                  },

                  /**
                   * Removes the node specified by {id} from the graph. Also removes
                   * all edges in the graph which point to the node.
                   * TODO: error handling (id does not exist)
                   */
                  removeNode : function (id) {
                          if (typeof this.nodes[id] !== "undefined") {
                                  this._removeEdgesTo(id);
                                  delete this.nodes[id];
                          } else {
                                  // throw error
                          }
                  },

                  /**
                   * Removes all edges from the graph which point to the node specified
                   * by {id}
                   */
                  _removeEdgesTo : function (id) {
                          for (var key in this.nodes) {
                                  this.nodes[key]._removeEdge(id);
                          }
                  },

                  /**
                   * Update coordinates for the node specified by {id} to {x}/{y}
                   */
                  setCoordinates : function (id, x, y) {
                          // validate input?
                          if (typeof this.nodes[id] !== "undefined") {
                                  this.nodes[id]._setCoordinates(x, y);
                          } else {
                                  // throw error, node not in GraphApp.Graph
                          }
                  },

                  /**
                   * Add an edge to the graph pointing from the node specified by
                   * {idfrom} to the node with {idto}
                   * TODO: enough?
                   */
                  addEdge : function (idfrom, idto) {
                          if (this.nodes[idfrom] && this.nodes[idto]) {
                                  this.nodes[idfrom]._addEdge(idto);
                          } else {
                                  // throw error, at least one id not in GraphApp.Graph
                          }
                  },

                  /**
                   * Removes the edge from the node with id {idfrom} pointing to the node
                   * with id {idto}
                   * TODO: error handlig
                   */
                  removeEdge : function (idfrom, idto) {
                          if (typeof this.nodes[idfrom] !== "undefined" && typeof this.nodes[idto] !== "undefined") {
                                  this.nodes[idfrom]._removeEdge(idto);
                          } else {
                                  // throw error, at least one id not in GraphApp.Graph
                          }
                  },

                  /**
                   * Returns the array with the node ids to which the node with {id} points
                   * to
                   */
                  neighbors : function (id) {
                          if (typeof this.nodes[id] !== "undefined") {
                                  return this.nodes[id]._neighbors();
                          } else {
                                  // throw error, node not in GraphApp.Graph
                          }
                  },
                  graphImport: function(fname){
                    //opening file
                    var req = new XMLHttpRequest();
                    req.open('GET', 'samples/' + fname, false);
                    req.send(null);

                    var jsonString = req.responseText;

                    // JSON.parse throws a SyntaxError when passed invalid JSON
                    try {
                      var data = Y.JSON.parse(jsonString);
                    }
                    catch (e) {
                      alert("Invalid data!");
                    }
                    var graph = data.graph;
                    if(graph !== 'undefined'){
                      // adding nodes
                      for (var i = graph.length - 1; i >= 0; --i) {
                        var n = this.addNode(
                          graph[i].value,
                          graph[i].x,
                          graph[i].y,
                          graph[i].id
                        );
                        graph[i].id = n.id; // overwrite id with the inserted node

                      }
                      // adding children
                      for (var i = graph.length - 1; i >= 0; --i) {
                        for(var c = graph[i].children.length -1 ; c >= 0; --c){
//                          alert(graph[i].id +" -> "+graph[i].children[c])
                          this.addEdge(graph[i].id,graph[i].children[c]);
                        }
                      }
                    }
                  }

                  // TODO: Import, Export
          };

          return {
                  Graph: Graph
          };
  }());
});